package com.yvon.maple.gateway.config;

import com.yvon.maple.gateway.component.DefaultAuthorizationManager;
import com.yvon.maple.gateway.component.DefaultSecurityContextRepository;
import com.yvon.maple.gateway.component.TokenAuthenticationManager;
import com.yvon.maple.gateway.handler.RestAuthenticationEntryPoint;
import com.yvon.maple.gateway.handler.RestfulAccessDeniedHandler;
import com.yvon.maple.gateway.properties.IgnoreProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod;
import org.springframework.security.authentication.DelegatingReactiveAuthenticationManager;
import org.springframework.security.authentication.ReactiveAuthenticationManager;
import org.springframework.security.config.annotation.web.reactive.EnableWebFluxSecurity;
import org.springframework.security.config.web.server.ServerHttpSecurity;
import org.springframework.security.web.server.SecurityWebFilterChain;
import reactor.core.publisher.Mono;

import javax.annotation.Resource;
import java.util.LinkedList;

/**
 * @author : Yvon
 * @since : 2021-11-26
 */
@Configuration
@EnableWebFluxSecurity
public class WebfluxSecurityConfig {


    private final DefaultAuthorizationManager authorizationManager;
    private final IgnoreProperties ignoreProperties;
    private final RestAuthenticationEntryPoint restAuthenticationEntryPoint;
    private final RestfulAccessDeniedHandler restfulAccessDeniedHandler;
    private final TokenAuthenticationManager tokenAuthenticationManager;


    public WebfluxSecurityConfig(DefaultAuthorizationManager authorizationManager, IgnoreProperties ignoreProperties, RestAuthenticationEntryPoint restAuthenticationEntryPoint, RestfulAccessDeniedHandler restfulAccessDeniedHandler, TokenAuthenticationManager tokenAuthenticationManager) {
        this.authorizationManager = authorizationManager;
        this.ignoreProperties = ignoreProperties;
        this.restAuthenticationEntryPoint = restAuthenticationEntryPoint;
        this.restfulAccessDeniedHandler = restfulAccessDeniedHandler;
        this.tokenAuthenticationManager = tokenAuthenticationManager;
    }


    @Resource
    private DefaultSecurityContextRepository defaultSecurityContextRepository;

    @Bean
    public SecurityWebFilterChain securityWebFilterChain(ServerHttpSecurity http) {
        http.authenticationManager(reactiveAuthenticationManager())
                .securityContextRepository(defaultSecurityContextRepository).httpBasic().disable().csrf().disable()
                .exceptionHandling()
                .and()
                // 请求拦截处理
                .authorizeExchange()
                .pathMatchers(ignoreProperties.getIgnoreUrl()).permitAll()
                .pathMatchers(HttpMethod.OPTIONS).permitAll()
                .anyExchange().access(authorizationManager)
                .and()
                .authenticationManager(tokenAuthenticationManager)
                // 认证通过后即可发起退出登录请求
                .exceptionHandling()
                .authenticationEntryPoint(restAuthenticationEntryPoint)
                //异常处理的响应
                .and()
                .exceptionHandling()
                //处理未授权
                .accessDeniedHandler(restfulAccessDeniedHandler)
                //CSRF处理
                .and().csrf().disable();
        return http.build();

    }

    /**
     * 注册用户信息验证管理器，可按需求添加多个按顺序执行
     */
    @Bean
    public ReactiveAuthenticationManager reactiveAuthenticationManager() {
        LinkedList<ReactiveAuthenticationManager> managers = new LinkedList<>();
        managers.add(authentication -> {
            // 其他登陆方式 (比如手机号验证码登陆) 可在此设置不得抛出异常或者 Mono.error
            return Mono.empty();
        });
        managers.add(tokenAuthenticationManager);
        return new DelegatingReactiveAuthenticationManager(managers);
    }


}
